﻿using log4net;
using log4net.Config;
using log4net.Repository;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Xml;
using System.IO;
using System.Text;

namespace ZFBActivation.Log
{
	public static class LogHelper
	{
		public delegate void LogEvel(LogLevel logDefinition, string msg);

		/// <summary>
		///     写日志
		/// </summary>
		public static event LogEvel OnLogEvel;

		#region < 变量 >

		//日志记录器,用于记录系统日志
		//private static readonly ILog _Log;

		#endregion < 变量 >

		#region < 静态构造 >

		/// <summary>
		///     静态构造函数,可能抛出异常
		/// </summary>
		[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
		[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
		private static readonly ILog m_Logger;

		static LogHelper() {
//#if DEBUG
			string resFileName = $"{System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace}.log4net.config";
			string content = ExtractResFile(Assembly.GetExecutingAssembly(), resFileName);
			ILoggerRepository repository = LogManager.CreateRepository("NETCoreRepository");
			XmlDocument xml = new XmlDocument();
			xml.LoadXml(content);
			XmlConfigurator.Configure(repository, xml.DocumentElement);

			m_Logger = LogManager.GetLogger(repository.Name, "");
//#endif

		}

		private static Dictionary<LogLevel, ILog> valuePairs = new Dictionary<LogLevel, ILog>();

		public static void CreateLogGroup(LogLevel def) {
//#if DEBUG

			string resFileName = $"{System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace}.template.config";
			string content = ExtractResFile(Assembly.GetExecutingAssembly(), resFileName);

			content = content.Replace("{KeyWord}", def.ToString());

			ILoggerRepository repository = LogManager.CreateRepository("NETCoreRepository" + def.ToString());
			XmlDocument xml = new XmlDocument();
			xml.LoadXml(content);
			XmlConfigurator.Configure(repository, xml.DocumentElement);

			valuePairs[def] = LogManager.GetLogger(repository.Name, "");
//#endif
		}

        /// <summary>
        /// 从资源文件中抽取资源文件
        /// </summary>
        /// <param name="resFileName">资源文件名称（资源文件名称必须包含目录，目录间用“.”隔开,最外层是项目默认命名空间）</param>
        /// <param name="outputFile">输出文件</param>
        public static string ExtractResFile(Assembly asm, string resFileName) {
            BufferedStream inStream = null;
            MemoryStream outStream = null;

            byte[] bomBuffer = new byte[] { 0xef, 0xbb, 0xbf };
            try {
                inStream = new BufferedStream(asm.GetManifestResourceStream(resFileName));
                outStream = new MemoryStream();

                byte[] buffer = new byte[1024];
                int length;

                while ((length = inStream.Read(buffer, 0, buffer.Length)) > 0) {
                    outStream.Write(buffer, 0, length);
                }
                outStream.Flush();
                var cache = outStream.ToArray();
                if (cache[0] == bomBuffer[0] && cache[1] == bomBuffer[1] && cache[2] == bomBuffer[2]) {
                    return Encoding.UTF8.GetString(cache, 3, cache.Length - 3);
                } else {
                    return Encoding.UTF8.GetString(cache);
                }
            }
            finally {
                outStream?.Close();
                inStream?.Close();
            }
        }


        private static string Key = string.Empty;

		public static void Init(string key) {
			Key = key;
		}

		#endregion < 静态构造 >

		#region < 日志记录方法,系统日志 >

		/// <summary>
		///     记录日志
		/// </summary>
		/// <param name="logDefinition">日志类型及消息定义</param>
		/// <param name="exception">异常对象(如果有异常)</param>
		/// <param name="args">格式化输出的实际参数</param>
		public static void Log(LogDefinition logDefinition, object msg) {
			try {
				LogCore(logDefinition.LogLevel, logDefinition.LogDesc, null, msg, null, true);
			} catch {
			}
		}

		/// <summary>
		///     记录日志
		/// </summary>
		/// <param name="logDefinition">日志类型及消息定义</param>
		/// <param name="exception">异常对象(如果有异常)</param>
		/// <param name="args">格式化输出的实际参数</param>
		public static void Log(LogDefinition logDefinition, params object[] msg) {
			try {
				LogCore(logDefinition.LogLevel, logDefinition.LogDesc, null, msg, null, true);
			} catch {
			}
		}

		/// <summary>
		///     记录日志
		/// </summary>
		/// <param name="logDefinition">日志类型及消息定义</param>
		/// <param name="exception">异常对象(如果有异常)</param>
		/// <param name="args">格式化输出的实际参数</param>
		public static void Log(LogDefinition logDefinition, Exception exception, params object[] msg) {
			try {
				LogCore(logDefinition.LogLevel, logDefinition.LogDesc, exception, msg, null, true);
			} catch {
			}
		}

		/// <summary>
		///     记录日志
		/// </summary>
		/// <param name="logDefinition">日志类型及消息定义</param>
		/// <param name="exception">异常对象(如果有异常)</param>
		/// <param name="args">格式化输出的实际参数</param>
		public static void Log(LogDefinition logDefinition, Exception exception, object msg) {
			try {
				LogCore(logDefinition.LogLevel, logDefinition.LogDesc, exception, msg, null, true);
			} catch (Exception ex) {
				Console.WriteLine("异常:" + exception.Message + ", 写日志异常:" + ex.Message);
			}
		}

		/// <summary>
		///     记录日志,为了堆栈统一,提供私有方法
		/// </summary>
		/// <param name="logLevel">日志级别</param>
		/// <param name="message">日志信息</param>
		/// <param name="exception">异常对象(如果有异常)</param>
		/// <param name="args">格式化输出的实际参数</param>
		[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
		private static void LogCore(LogLevel logLevel, string message, Exception exception, object msg, object data, bool evel) {
			//先判断是否需要记录,如果不需记录则直接返回,不再进行后续处理

			//进行日志消息格式化处理,并对非基本对象进行并行序列化处理
			string dataString = string.Empty;
			try {
				//如果提供了待格式化串和参数则进行格式化处理

				if (msg != null) {
					Type msgType = msg.GetType();

					if (msgType.IsArray) {
						object[] msgArray = (object[])msg;
						foreach (var item in msgArray) {
							if (item == null) continue;
							var itemType = item.GetType();
							if (itemType.IsArray) {
								object[] itemArray = item as object[];
								foreach (var itemObj in itemArray) {
									if (itemObj == null) continue;
									var itemObjType = itemObj.GetType();

									if (!itemObjType.IsPrimitive && itemObjType.IsClass && itemObjType.Name != "String")
										dataString += JsonConvert.SerializeObject(itemObj) + " ";
									else {
										dataString += itemObj.ToString() + " ";
									}
								}
							} else {
								if (!itemType.IsPrimitive && itemType.IsClass && itemType.Name != "String")
									dataString += JsonConvert.SerializeObject(item) + " ";
								else {
									dataString += item.ToString() + " ";
								}
							}

						}
					} else {
						if (!msgType.IsPrimitive && msgType.IsClass && msgType.Name != "String")
							dataString += JsonConvert.SerializeObject(msg) + " ";
						else {
							dataString += msg.ToString() + " ";
						}
					}
				}

				if (data != null) {
					var dataType = data.GetType();
					if (dataType.IsArray) {
						object[] dataArray = (object[])data;
						foreach (var item in dataArray) {
							if (item == null) continue;
							var itemType = item.GetType();
							if (itemType.IsArray) {
								object[] itemArray = item as object[];
								foreach (var itemObj in itemArray) {
									if (itemObj == null) continue;
									var itemObjType = itemObj.GetType();

									if (!itemObjType.IsPrimitive && itemObjType.IsClass && itemObjType.Name != "String")
										dataString += JsonConvert.SerializeObject(itemObj) + " ";
									else {
										dataString += itemObj.ToString() + " ";
									}
								}
							} else {
								if (!itemType.IsPrimitive && itemType.IsClass && itemType.Name != "String")
									dataString += JsonConvert.SerializeObject(item) + " ";
								else {
									dataString += item.ToString() + " ";
								}
							}
						}
					} else {
						if (!dataType.IsPrimitive && dataType.IsClass && dataType.Name != "String")
							dataString += JsonConvert.SerializeObject(data) + " ";
						else {
							dataString += data.ToString() + " ";
						}
					}
				}
			} catch {
				//m_Logger.Fatal(string.Format(CultureInfo.InvariantCulture, "日志信息格式化错误, Message:{0}", message), ex);
			}

			var nowTime = DateTime.Now;
			//根据日志级别写入对应日志
			switch (logLevel) {
				case LogLevel.Verbose:
					Debug.WriteLine($"{nowTime}  {dataString}");
					m_Logger.Debug(Key + " " + dataString, exception);
					break;

				case LogLevel.Info:
					Debug.WriteLine($"{nowTime}  {dataString}");
					m_Logger.Info(Key + " " + dataString, exception);
					break;

				case LogLevel.Warn:
					Debug.WriteLine($"{nowTime}  {dataString}");
					m_Logger.Warn(Key + " " + dataString, exception);
					break;

				case LogLevel.Error:
					Debug.WriteLine($"{nowTime}  {dataString} {exception?.Message} {exception?.StackTrace}");
					m_Logger.Error(Key + " " + dataString, exception);
					break;

				default:
					valuePairs[logLevel].Debug(Key + " " + dataString, exception);
					Debug.WriteLine($"{nowTime}  {dataString} {exception?.Message} {exception?.StackTrace}");
					break;
			}

			if (evel) {
				OnLogEvel?.Invoke(logLevel, dataString);
			}
		}

		#endregion < 日志记录方法,系统日志 >
	}
}